This chapter describes the facility of KCL to interface the C language and KCL. With this facility, the user can arrange his or her
C-language programs so that they can be invoked from KCL. In
addition, the user can write Lisp function definitions in the C
language to increase runtime efficiency.
The basic idea of interfacing the C language is this: As mentioned
in Chapter 6, the KCL compiler, given a Lisp source file, creates an
intermediate C-language program file, called c-file, which is
then compiled by the C-language compiler to obtain the final
fasl-file. Usually, the c-file consists of C-language function
definitions. The first C-language function in the c-file is the
``initializer'', which is executed when the fasl file is loaded, and
the other C-language functions are the C versions of the Lisp
functions (including macro expansion functions) defined in the source
file. By using the top-level macros Clines and defCfun
described below, the user can direct the compiler to insert his or her
own C-language function definitions and/or C-language preprocessor
macros such as #define and #include into the c-file. In
order that such C-language functions be invoked from KCL, another
top-level macro defentry is used. This macro defines a Lisp
function whose body consists of the calling sequence to the specified
C-language function.
The C-language function definitions are placed in the c-file in the
order of the corresponding Lisp functions defined
in the source file. That is, the C code for the first Lisp function comes
first, the C code for the second Lisp function comes second, and so on. If
a Clines or defCfun macro form appears between two
Lisp function definitions in the source file, then the C code specified by
the macro is placed in between the C code for the Lisp functions.
We define some terminology here which is used throughout
this Chapter. A C-id is either a Lisp string consisting of a
valid C-language identifier, or a Lisp symbol
whose print-name, with all its alphabetic characters turned into lower case,
is a valid C identifier. Thus the
symbol foo is equivalent to the string ``foo''
when used as a C-id. Similarly, a C-expr is a
string or a symbol that may be regarded as a C-language expression. A C-type is one of
the Lisp symbols int, char, float, double, and object. Each
corresponds to a data
type in the C language; object is the type of Lisp object and
other C-types are primitive
data types in the C language.
Clines {string}*[Macro]
- When the KCL compiler encounters a macro form (Clines
string1 ... stringn), it
simply outputs the strings into the c-file. The arguments are not evaluated and
each argument must be a string. Each string may consist of any number of lines,
and separate lines in the string are placed in separate lines in the c-file. In addition,
each string opens a fresh line in the c-file, i.e., the first character
in the string is
placed at the first column of a line. Therefore, C-language preprocessor commands
such as #define and #include will be recognized as such by
the C compiler, if the ' # ' sign
appears as the first character of the string or as the first
character of a line within the string.
- In order to clearly distinguish C code from other parts of Lisp programs,
we, the implementors of KCL, make it our rule to start each C code line
with a percent sign ' % '. We define % as a read macro
which returns the rest of the line as a string. For example,
;;; C version of TAK.
(Clines
% int tak(x, y, z)
% int x, y, z;
% { if (y >= x) return(z);
% else return(tak(tak(x-1, y, z),
% tak(y-1, z, x),
% tak(z-1, x, y)));
% }
)
- Of course, the user may instead enclose each C code line or the
whole C code with double quotes, but we recommend the use of
the percent sign read macro. Since
the percent sign read macro is not a standard read macro, the users must
define this read macro by themselves. We use the following definition.
(set-macro-character
#\\%
#'(lambda (stream char) (values (read-line stream)))))
- Here, the lambda-expression returns the first value of read-line by
using values as a filter.
- When interpreted, a Clines macro form expands to nil.
defentry function parameter-list C-function[Macro]
- defentry defines a Lisp function whose body consists of
the calling sequence to a
C-language function. function is the name of the Lisp function
to be defined, and C-function specifies the C function
to be invoked. C-function must be either a list
(type C-id) or C-id,
where type and C-id are the type and
the name of the C function. type must be a C-type or
the symbol void which means that the C function returns
no value. (object C-id ) may be abbreviated
as C-id. parameter-list is a list of C-types
for the parameters of the C function. For example,
the following defentry
form defines a Lisp function tak from
which the C function tak above is called.
(defentry tak (int int int) (int tak))
- The Lisp function tak defined by this defentry form
requires three arguments. The arguments are converted to
int values before they are passed to the C function. On return
from the C
function, the returned int value is converted
to a Lisp integer (actually a fixnum)
and this fixnum will be returned as the value
of the Lisp function. See below for
type conversion between Lisp and the C language.
- A defentry form is treated in the above way only when it appears
as a top-level form of a Lisp source file. Otherwise, a defentry form
expands to nil.
defla name lambda-list {declaration | doc-string}*
{form}*[Macro]
- When interpreted, defla is exactly the same as defun.
That is, (defla name
lambda-list . body)
expands to (defun name lambda-list . body) . However,
defla forms are completely ignored by the compiler; no C-language code will be
generated for defla forms. The primary use of defla is to define
a Lisp function in two ways within a single Lisp source file; one in the C language and the other in
Lisp. defla is short for DEFine Lisp Alternative.
- Suppose you have a Lisp source file whose contents are:
;;; C version of TAK.
(Clines
% int tak(x, y, z)
% int x, y, z;
% { if (y >= x) return(z);
% else return(tak(tak(x-1, y, z),
% tak(y-1, z, x),
% tak(z-1, x, y)));
% }
)
;;; TAK calls the C function tak defined above.
(defentry tak (int int int) (int tak))
;;; The alternative Lisp definition of TAK.
(defla tak (x y z)
(if (>= y x)
z
(tak (tak (1- x) y z)
(tak (1- y) z x)
(tak (1- z) x y))))
- When this file is loaded into KCL, the interpreter uses the Lisp
version of the tak definition. Once this file has been compiled, and
when the generated fasl file is
loaded into KCL, a function call to tak is actually the call to the C version
of tak.
defCfun header n {element}*[Macro]
- defCfun defines a C-language function which calls
Lisp functions and/or which
handles Lisp objects. header is a string consisting of the C
code for
- the optional type-specifier of the C function,
- the function-declarator of the C function, and
- the type-decl-list of the parameters to the C function.
- (For the C-language terminology, refer to The C Programming
Language by Brian W. Kernighan and Dennis M. Ritchie.) The rest of the C
function definition, i.e., the
function-statement, is given by elements. Each element
may be a string, in which case
the string is treated in the same way as the arguments to the Clines
macro. Or else, the element is a list
((name arg1 ... argn ) place1 ... placem ) . The compiler
translates this list into a calling sequence to the Lisp function
whose name is name. As will be
mentioned later, name may be quote,
but name may not be the name of any other special
form or a macro. The args specify the arguments to the function
and the places
specify where the values should go. Thus the list-formed element could
be regarded as something like the Lisp form:
(multiple-value-setq
( place1 ... placem )
( name arg1 ... argn )).
- Each arg is a list (C-type C-expr), where C-expr is any C-language expression of the
type C-type. If type is object, then arg may be
written simply as C-expr. Similarly,
each place is a list (C-type C-expr), or it may be abbreviated
as C-expr if C-type is
object. The C-expr in this case is any lvalue (in the
terminology of the C language), i.e., it may be any valid C-language code that can be written at the left
side of an assignment.
- The function call is performed as follows. The args are
evaluated, and the
values are sent to the specified Lisp function after type conversion from C to Lisp. On return from the called Lisp function, each returned value is assigned to the
corresponding place, i.e., the first returned value goes to place1, the second to place2,
and so on. If there are more places than the values returned, extra
values of nil
are assigned to the remaining places. If there are more values than
places, the
excess values are simply discarded. If necessary, Lisp-to-C type conversion may
take place before each returned value is assigned.
- If the Lisp function is called just for side-effects, then the
list-formed element
may be abbreviated as a one-level list (name arg1 ... argn).
- As a special case, if a list-formed element is of the form
((quote value) place),
the Lisp object value is assigned to place. Here value
may be any Lisp object.
- The following defCfun form defines the C function silly which
adds 100 to the value of the parameter x and prints the result in three different ways. The
second argument to defCfun will be described later, and the user may ignore it.
(defCfun ``silly(x) int x;'' 0
% int y;
((+ (int x) (int ``100'')) (int y))
% printf(``\\n%d'', y);
% y = x+100;
(print (int y))
(print (int ``x+100''))
)
- When a C function handles Lisp objects (i.e., data of type object), the user
should be careful enough so that the objects may not be garbage-collected. This is
because the garbage collector of KCL does not take care of Lisp objects used in the
C function. See the following C function which is assumed to return a two-element
list consisting of its two arguments.
(defCfun ``object list2(x,y) object x,y;'' 0
% object z;
('nil z)
((cons y z) z)
((cons x z) z)
% return(z);
)
- When invoked, list2 first sets nil to the variable
z, conses y to z, and then conses
x. Each time cons is called, a new cons cell is allocated and the pointer to this cell
is stored in z. However, there is no way to inform
the garbage collector that the cells are referenced from the C variable
z. Suppose
that the cons cell allocated by the first cons is the last cons cell available at that
time. Then, during execution of the second call to cons, the garbage collector
begins to run and, unfortunately, the cons cell in z will be destroyed so that the
cell can be recycled for further use.
- To prevent a Lisp object from being unexpectedly garbage collected, the user
must save the object in some place that is recognized by the garbage collector. The
second parameter n to defCfun is used to reserve n such
places for each call to the
C function. In the body of the C function, these reserved places are referenced as
vs[0], ..., vs[n-1]. The function list2 above, therefore,
should be revised as follows.
(defCfun ``object list2(x,y) object x,y;'' 1
('nil ``vs[0]'')
((cons y ``vs[0]'') ``vs[0]'')
((cons x ``vs[0]'') ``vs[0]'')
% Creturn(vs[0]);
)
- Notice that return is replaced by Creturn. Creturn is similar to return except
that Creturn releases the reserved places on
return from the function. In the C
code within a defCfun form,
write ``Creturn(value);'' instead of
``return(value);'', and
write ``Cexit;'' instead of ``return;''.
- Again, a defCfun form has the above meaning only when it appears
as a top-level form in a Lisp source file. Otherwise, the form expands to
nil.
KCL converts a Lisp object into a C-language data by using the Common Lisp function coerce: For the C-type int (or char), the object is first coerced to a Lisp integer and the least significant 32-bit (or
8-bit) field is used as the C int (or char). For the
C-type float (or double), the object is coerced to a
short-float (or a long-float) and this value is used as the C
float (or double). Conversion from a C data into a Lisp object
is obvious: C char, int, float, and double become the
equivalent Lisp character, fixnum, short-float, and long-float,
respectively.
Here we list the complete syntax of Clines, defentry, and
defCfun macro forms.
Clines-form:
(Clines { string }* )
defentry-form:
(defentry function-symbol
( { C-type }* )
{ C-function-name | ( { C-type | void } C-function-name ) } )
defCfun-form:
(defCfun string non-negative-integer
{ string
| ( function-symbol { value }* )
| (( function-symbol { value }* ) { place }* ) } )
value:
place:
{ C-expr | ( C-type C-expr ) }
C-function-name:
C-expr:
{ string | symbol }
C-type:
{ object | int | char | float | double }